package org.seedltd.upms.service.impl.sys;

import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.seedltd.base.exception.BusinessException;
import org.seedltd.core.base.BasePage;
import org.seedltd.core.utils.*;
import org.seedltd.springsecurity.SeedltdUserDetails;
import org.seedltd.springsecurity.server.WeCharUserDetailsService;
import org.seedltd.springsecurity.utils.UserUtils;
import org.seedltd.upms.constant.GlobalConst;
import org.seedltd.upms.enums.UserEnableStatus;
import org.seedltd.upms.mapper.sys.SysUserMapper;
import org.seedltd.upms.model.sys.entity.SysCompany;
import org.seedltd.upms.model.sys.entity.SysDept;
import org.seedltd.upms.model.sys.entity.SysRole;
import org.seedltd.upms.model.sys.entity.SysUser;
import org.seedltd.upms.model.sys.form.SysUserForm;
import org.seedltd.upms.model.sys.form.SysUserSearchForm;
import org.seedltd.upms.model.sys.vo.SysUserVO;
import org.seedltd.upms.service.sys.SysCompanyService;
import org.seedltd.upms.service.sys.SysDeptService;
import org.seedltd.upms.service.sys.SysRoleService;
import org.seedltd.upms.service.sys.SysUserService;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.collect.Lists;

import lombok.AllArgsConstructor;

/**
 * 用户管理带缓存的业务实现
 *
 * @author laizuan
 * @version 1.0
 * @since 2021/07/19
 */
@Service
@AllArgsConstructor
@CacheConfig(cacheNames = "SysUser#12d")
public class SysUserServiceImpl implements SysUserService, UserDetailsService, WeCharUserDetailsService {
    private final SysUserMapper sysUserMapper;
    private final SysCompanyService sysCompanyService;
    private final PasswordEncoder passwordEncoder;
    private final SysRoleService sysRoleService;
    private final SysDeptService sysDeptService;

    @Override
    @Cacheable(key = "#id", unless = "#result == null")
    public SysUser findById(Serializable id) {
        return sysUserMapper.selectById(id);
    }

    @Override
    @Transactional
    @CacheEvict(key = "#form.userId")
    public boolean update(SysUserForm form) {
        final Long userId = form.getUserId();
        SysUser sysUser = this.findById(userId);
        Assert.notNull(sysUser, "未找到需要修改的用户数据");
        this.checkUserIsCancel(sysUser);

        BeanUtils.copyProperties(form, sysUser, "password", "loginName");

        if (StringUtils.isNotBlank(form.getPassword())) {
            sysUser.setPassword(passwordEncoder.encode(form.getPassword()));
        }
        boolean success = SqlUtils.retBool(sysUserMapper.updateById(sysUser));
        if (success) {
            this.sysUserMapper.deleteUserRoleByUserId(userId);
            this.sysUserMapper.deleteUserDeptByUserId(userId);
            this.sysUserMapper.deleteUserPostByUserId(userId);

            List<Long> roleIdList = form.getRoleIdList();
            List<Long> deptIdList = form.getDeptIdList();

            sysUserMapper.insertUserDept(userId, deptIdList);
            sysUserMapper.insertUserRole(userId, roleIdList);
        }
        return success;
    }

    private void checkUserIsCancel(SysUser sysUser) {
        UserEnableStatus enabled = sysUser.getEnabled();
        if (UserEnableStatus.CANCEL.equals(enabled)) {
            throw new BusinessException("用户已注销不能操作");
        }
    }

    @Override
    @CacheEvict(key = "#id")
    public boolean delete(Serializable id) {
        this.sysUserMapper.deleteUserRoleByUserId(id);
        this.sysUserMapper.deleteUserDeptByUserId(id);
        return SqlUtils.retBool(sysUserMapper.deleteById(id));
    }

    @Override
    @Transactional
    @CachePut(key = "#result.userId", unless = "#result.userId == null")
    public SysUser add(SysUserForm form) {
        String loginName = form.getLoginName();
        if (this.existsLoginName(loginName)) {
            throw new BusinessException(String.format("登录账号【%s】已存在，请重新更换", loginName));
        }
        SysUser sysUser = new SysUser();
        BeanUtils.copyProperties(form, sysUser);

        SeedltdUserDetails loginUser = UserUtils.getLoginUser();
        sysUser.setCompanyId(loginUser.getCompanyId());
        if (StringUtils.isNotBlank(form.getPassword())) {
            sysUser.setPassword(passwordEncoder.encode(form.getPassword()));
        } else {
            throw new BusinessException("请填写用户密码");
        }

        sysUserMapper.insert(sysUser);
        List<Long> roleIdList = form.getRoleIdList();
        List<Long> deptIdList = form.getDeptIdList();
        final Long userId = sysUser.getUserId();
        sysUserMapper.insertUserDept(userId, deptIdList);
        sysUserMapper.insertUserRole(userId, roleIdList);
        return sysUser;
    }

    @Override
    public BasePage<SysUserVO> page(SysUserSearchForm searchForm) {
        return sysUserMapper.selectSysUserList(BasePage.of(searchForm.getStart(), searchForm.getSize()), searchForm);
    }

    private SysUser getByLoginName(String loginName) {
        return SqlUtils.q(sysUserMapper).eq(SysUser::getLoginName, loginName).selectOne();
    }

    private SysUser getByOpenId(String openId) {
        return SqlUtils.q(sysUserMapper).eq(SysUser::getWxOpenId, openId).selectOne();
    }

    @Override
    public boolean updateLastLoginTime(Long id) {
        SysUser sysUser = new SysUser();
        sysUser.setUserId(id);
        sysUser.setLastLoginTime(new Date());
        return sysUserMapper.updateById(sysUser) > 0;
    }

    @Override
    public Set<Long> listUserRoleByUserId(Long userId) {
        return sysUserMapper.listUserRoleByUserId(userId);
    }

    @Override
    public Set<Long> listUserDeptByUserId(Long userId) {
        return sysUserMapper.listUserDeptByUserId(userId);
    }

    @Override
    @Transactional
    public boolean updateUserStatusById(Long userId, Integer status) {
        SysUser sysUser = this.findById(userId);
        checkUserIsCancel(sysUser);

        UserEnableStatus enumByKey = EnumUtils.getEnumByKey(UserEnableStatus.class, status);
        if (enumByKey == null) {
            throw new BusinessException("状态无效");
        }

        if (enumByKey.equals(sysUser.getEnabled())) {
            return true;
        }

        SysUser updateModel = new SysUser();
        updateModel.setUserId(userId);
        updateModel.setEnabled(enumByKey);

        return sysUserMapper.updateById(updateModel) > 0;
    }

    private boolean existsLoginName(String loginName) {
        return sysUserMapper.existsByLoginName(loginName) != null;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SysUser sysUser = this.getByLoginName(username);
        return getSeedltdUserDetails(sysUser);
    }

    private SeedltdUserDetails getSeedltdUserDetails(SysUser sysUser) {
        if (sysUser == null) {
            throw new UsernameNotFoundException("用户名或密码不正确");
        }
        SysCompany sysCompany = sysCompanyService.findById(sysUser.getCompanyId());
        String companyCode = Optional.ofNullable(sysCompany).map(SysCompany::getCompanyCode)
            .orElseThrow(() -> new UsernameNotFoundException("用户企业无效"));

        Set<String> roles = sysUserMapper.listRoleCodeByUserId(sysUser.getUserId());
        Set<String> permissions = sysUserMapper.listPermissionByRoleCodeList(roles);
        permissions = permissions.stream().filter(StringUtils::isNotBlank).collect(Collectors.toSet());

        // @formatter:off
        return SeedltdUserDetails.builder()
                .nickName(sysUser.getNickName())
                .userId(sysUser.getUserId())
                .companyCode(companyCode)
                .companyId(sysCompany.getCompanyId())
                .loginName(sysUser.getLoginName())
                .password(sysUser.getPassword())
                .permission(StringUtils.toStringArray(permissions))
                .roles(StringUtils.toStringArray(roles))
                .accountExpired(false)
                .credentialsExpired(false)
                .disabled(UserEnableStatus.CANCEL.equals(sysUser.getEnabled()))
                .accountLocked(UserEnableStatus.DISABLE.equals(sysUser.getEnabled()))
                .build();
        // @formatter:on
    }

    @Override
    @Transactional
    public UserDetails loadUserByOpenId(String s) throws UsernameNotFoundException {
        SysUser sysUser = this.getByOpenId(s);
        if (sysUser == null) {
            sysUser = new SysUser();
            // 如果用户不存在则注册一个用户
            sysUser.setCompanyId(1L);
            sysUser.setLoginName(s);
            sysUser.setPassword(passwordEncoder.encode(s));
            sysUser.setNickName("微信用户");
            sysUser.setWxOpenId(s);
            sysUser.setLastLoginTime(new Date());
            sysUser.setEnabled(UserEnableStatus.ENABLE);
            sysUserMapper.insert(sysUser);

            final Long userId = sysUser.getUserId();
            SysRole sysRole = sysRoleService.findByRoleCode(GlobalConst.MINI_APP_USER_ROLE_CODE);
            SysDept sysDept = sysDeptService.findByDeptCode(GlobalConst.MINI_APP_USER_DEPT_CODE);
            if (sysDept != null) {
                sysUserMapper.insertUserDept(userId, Lists.newArrayList(sysDept.getDeptId()));
            }
            if (sysRole != null) {
                sysUserMapper.insertUserRole(userId, Lists.newArrayList(sysRole.getRoleId()));
            }
        }
        return getSeedltdUserDetails(sysUser);
    }
}